// File Name:    ReversiModel.java
// Description:   ****This is a Model class used to define all kinds of logic and algorithm****


package main.reversi.model;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;


public class ReversiModel {
  public static int BOARD_SIZE = 9;
    
  public static ObjectProperty<Owner>[][] board = new ObjectProperty[BOARD_SIZE][BOARD_SIZE];
  public static ObjectProperty<Holder>[][] back = new ObjectProperty[BOARD_SIZE][BOARD_SIZE];
  public static ObjectProperty<Owner>[] ball = new ObjectProperty[3];
  
  private static int score=0;
  static SimpleStringProperty scre = new SimpleStringProperty(""+score);

  public static SimpleStringProperty scoreProperty(){
	  return scre;
  }
  public static void restart() {
	    for (int i = 0; i < BOARD_SIZE; i++) {
	        for (int j = 0; j < BOARD_SIZE; j++) {
	          board[i][j].setValue(Owner.NONE);
	          back[i][j].setValue(Holder.SQUARE);
	        }
	      }
	    count = 0;
	    score=0;
        scre.setValue(""+score);
        next3ball();
        addRadomBalls(5);
 }
  //constructor
  public ReversiModel() {
    for (int i = 0; i < BOARD_SIZE; i++) {
        for (int j = 0; j < BOARD_SIZE; j++) {
          board[i][j] = new SimpleObjectProperty<Owner>(Owner.NONE);  
          back[i][j] = new SimpleObjectProperty<Holder>(Holder.SQUARE);
        }
    }
    for(int i=0;i<3;i++)
    	ball[i]=new SimpleObjectProperty<Owner>(Owner.NONE);  
    next3ball();
    addRadomBalls(5);
    count = 0;
  }

 //initializer
  public static void oneturn() {
       addNextBall();
       next3ball();
  }
  private static void next3ball(){
	  for(int i=0;i<3;i++)
		  ball[i].setValue(Owner.radomBall());
	 
  }
  private static  void addNextBall(){
	  for(int i=0;i<3;i++){
		  int x=(int)(Math.random()*100/9);
		  int y=(int)(Math.random()*100/9);
		  //�õ�һ����λ�õ����
		  while(!isEmptyCell(x,y)){
			  x=(int)(Math.random()*100/9);
			  y=(int)(Math.random()*100/9);
		  }
		  board[x][y].setValue(ball[i].get());
	  }
  }
  private static  void addRadomBalls(int time){
	  for(int i=0;i<time;i++){
		  int x=(int)(Math.random()*100/9);
		  int y=(int)(Math.random()*100/9);
		  //get an empty position
		  while(!isEmptyCell(x,y)){
			  x=(int)(Math.random()*100/9);
			  y=(int)(Math.random()*100/9);
		  }
		  board[x][y].setValue(Owner.radomBall());
	  }
  }
  public static boolean isInBoard(int cellX,int cellY){
	 if(cellX < BOARD_SIZE && cellX>=0 && cellY < BOARD_SIZE && cellY>=0  )
		 return true;
	 return false;		 
 }
  //check is empty
  public static boolean isEmptyCell(int cellX,int cellY){
	 if(cellX < BOARD_SIZE && cellX>=0 && cellY < BOARD_SIZE && cellY>=0  )
	   return board[cellX][cellY].isEqualTo(Owner.NONE).get();
	 else {
		return false;
	}
  }
  public static boolean isFull(){
	  int total=0;
	  for (int i = 0; i < BOARD_SIZE; i++) {
	        for (int j = 0; j < BOARD_SIZE; j++) {
	        	if(!isEmptyCell(i,j))
	              total++;  
	        }
	  }
	  
	  if( total<=78) return false;
	  else{		 
		  return true;
	  }
  }

    public static int count=0; //count = 1 means have clicked once
    static int x;
    static int y;
    static int combo = 1;
    //the play function is used to check the times of click,and whether it should move
  public boolean play(int cellX, int cellY) {    
    //������ѡ�����ʱ�����ѡ��״̬
	back[x][y].setValue(Holder.SQUARE);
	
	if(!isInBoard(cellX,cellY) || isFull()){
		System.out.println("����");
		return false;
	}
	
    if(!isEmptyCell(cellX,cellY)){
    	count = 1;
    	x = cellX; y = cellY;
		back[x][y].setValue(Holder.SELECT);//����ѡ��״̬
		return false;
    }
    
    else if(count == 1 && isEmptyCell(cellX,cellY)) {
    	//��ʾ·��
    	if(way(cellX,cellY,x,y)){
    		//�ܹ�ͨ��ʱ���ý���
    		board[cellX][cellY].setValue(board[x][y].getValue());
    		board[x][y].setValue(Owner.NONE);
    		back[x][y].setValue(Holder.SQUARE);  		
    		//������ 	
        }
    	initback();
		initRecord();
    	count = 0;
    	return true;
    }
    return false;

  }

  private static boolean ispure(Owner board2){
	  if(board2==Owner.BLACK||board2==Owner.BLUE||board2==Owner.GREEN||board2==Owner.ORANGE||board2==Owner.RED||board2==Owner.WHITE||board2==Owner.YELLOW){
		  return true;
	  }
	  return false;
  }
  
  public static void vanishWild(int cellX,int cellY){
	     Owner temp = board[cellX][cellY].get();
		if(ispure(temp) && vanish(cellX,cellY,temp)){
			combo++;
		}
		
		//consider the special condition
		else if(temp.equals(Owner.WILD) || temp.equals(Owner.BOMB)){
			temp = Owner.WHITE;
			if(vanish(cellX,cellY,temp)) combo++;
			else{
				temp = Owner.BLACK;
				if(vanish(cellX,cellY,temp)) combo++;
				else{
					temp = Owner.BLUE;
					if(vanish(cellX,cellY,temp)) combo++;
					else{
						temp = Owner.GREEN;
						if(vanish(cellX,cellY,temp)) combo++;
						else{
							temp = Owner.ORANGE;
							if(vanish(cellX,cellY,temp)) combo++;
							else{
								temp = Owner.RED;
								if(vanish(cellX,cellY,temp)) combo++;
								else{
									temp = Owner.YELLOW;
									if(vanish(cellX,cellY,temp)) combo++;
								}
							}
						}
					}
				}
			}
		}
		else if(temp.equals(Owner.BLUEWHITE)){
			temp = Owner.BLUE;
			if(vanish(cellX,cellY,temp)) combo++;
			else{
				temp = Owner.WHITE;
				if(vanish(cellX,cellY,temp)) combo++;
			}
		}
		else if(temp.equals(Owner.REDORANGE)){
			temp = Owner.RED;
			if(vanish(cellX,cellY,temp)) combo++;
			else{
				temp = Owner.ORANGE;
				if(vanish(cellX,cellY,temp)) combo++;
			}
		}
		else if(temp.equals(Owner.GREENYELLOW)){
			temp = Owner.GREEN;
			if(vanish(cellX,cellY,temp)) combo++;
			else{
				temp = Owner.YELLOW;
				if(vanish(cellX,cellY,temp)) combo++;
			}
		}
		else combo = 1;

  }
private static boolean vanish(int a,int b,Owner color){
		//up an down direction
		int temp = 1;
		int up = 0;
		while(isInBoard(a,b-temp)&& isSame(color,board[a][b-temp].get()) && up<4 ){
			up++;
			temp++;
		}

		temp = 1;
		int down = 0;
		while(isInBoard(a,b+temp)&& isSame(color,board[a][b+temp].get()) && down<4 ){
			down++;
			temp++;
		}
		
		//left and right position
		temp = 1;
		int left = 0;
		while(isInBoard(a-temp,b)&&isSame(color,board[a-temp][b].get()) && left<4 ){
			left++;
			temp++;
		}
		
		temp = 1;
		int right = 0;
		while(isInBoard(a+temp,b)&& isSame(color,board[a+temp][b].get()) && right<4 ){
			right++;
			temp++;
		}

		//���ϵ�����
		temp = 1;
		int ll = 0;
		while(isInBoard(a-temp,b-temp)&&isSame(color,board[a-temp][b-temp].get()) && ll<4 ){
			ll++;
			temp++;
		}
		
		temp = 1;
		int rr = 0;
		while(isInBoard(a+temp,b+temp)&& isSame(color,board[a+temp][b+temp].get()) && rr<4 ){
			rr++;
			temp++;
		}

		//���µ�����
		temp = 1;
		int l = 0;
		while(isInBoard(a-temp,b+temp)&&isSame(color,board[a-temp][b+temp].get()) && l<4 ){
			l++;
			temp++;
		}
					
		temp = 1;
		int r = 0;
		while(isInBoard(a+temp,b-temp)&& isSame(color,board[a+temp][b-temp].get()) && r<4 ){
			r++;
			temp++;
		}
			
					
		//vanish
		int lines = 1;
		int forScore =score;
		Owner color1 = Owner.NONE;
		if(up + down >= 4 ){
			for(int i=-up;i<=down;i++){
				if(isInBoard(a,b+i)){
					//set center color
					if(ispure(board[a][b+i].get())){
						color1 = board[a][b+i].get();
						System.out.printf("�鵽��ɫ��(%d,%d)",a,b+i);
					}
				}
			}
			for(int i=-up;i<=down;i++){
				if(isInBoard(a,b+i)&& board[a][b+i].isEqualTo(Owner.BOMB).get())
					clear(color1);
					//clear balls in line
					board[a][b+i].setValue(Owner.NONE);
			}

			score += (3+2*(up+down-4))*lines*combo;
			lines++;
		}
		if(left + right >= 4){
			for(int i=-left;i<=right;i++){
				if(isInBoard(a+i,b)){
					if(ispure(board[a+i][b].get())){
						color1= board[a+i][b].get();
						System.out.printf("�鵽��ɫ��(%d,%d)",a+i,b);
					}
				}
			}
			for(int i=-left;i<=right;i++){
				if(isInBoard(a+i,b)&& board[a+i][b].isEqualTo(Owner.BOMB).get())
					clear(color1);
					board[a+i][b].setValue(Owner.NONE);
			}
			score += (3+2*(left+right-4))*lines*combo;
			lines++;
		}
		if(ll + rr >= 4){
			for(int i=-ll;i<=rr;i++){
				if(isInBoard(a+i,b+i)){
					//������ɫ
					if(ispure(board[a+i][b+i].get())){
						color1=board[a+i][b+i].get();
						System.out.printf("�鵽��ɫ��(%d,%d)",a+i,b+i);
					}
				}
			}
			for(int i=-ll;i<=rr;i++){
				if(isInBoard(a+i,b+i)&& board[a+i][b+i].isEqualTo(Owner.BOMB).get())
					clear(color1);
				//�����������
				board[a+i][b+i].setValue(Owner.NONE);
			}
			score += (3+2*(ll+rr-4))*lines*combo;
			lines++;
		}
		if(l + r >= 4){
			for(int i=-l;i<=r;i++){
				if(isInBoard(a+i,b-i)){
					//��������ɫ
					if(ispure(board[a+i][b-i].get())){
						color1=board[a+i][b-i].get();
						System.out.printf("�鵽��ɫ��(%d,%d)",a+i,b-i);
						
					}
				}	
			}
			for(int i=-l;i<=r;i++){
				if(isInBoard(a+i,b-i)&& board[a+i][b-i].isEqualTo(Owner.BOMB).get())
					clear(color1);
				//�����������
				board[a+i][b-i].setValue(Owner.NONE);
			}
			
			score += (3+2*(l+r-4))*lines*combo;
			lines++;
		}
		
		scre.setValue(""+score);
		
		//�ж��Ƿ�ɹ����
		if(forScore == score)
			return false;
		else 
			return true;	  	
		
	}	
	
 
  //remove one color from the board
  private static void clear(Owner color){
	  for(int i=0;i<9;i++)
		  for(int j=0;j<9;j++)
			  if(board[i][j].get() == color)
				  board[i][j].setValue(Owner.NONE);
  }
  
  public static boolean isSame(Owner a,Owner b){
	  if(a.equals(b)) return true;
	  //different colors
	  if((a.equals(Owner.BLUE) ||a.equals(Owner.WHITE) ) &&  b.equals(Owner.BLUEWHITE) )
		  return true;
	  if((a.equals(Owner.GREEN) ||a.equals(Owner.YELLOW) ) &&  b.equals(Owner.GREENYELLOW) )
		  return true; 
	  if((a.equals(Owner.RED) || a.equals(Owner.ORANGE) ) &&  b.equals(Owner.REDORANGE) )
		  return true;
	
	  
	  if((b.equals(Owner.BLUE) ||b.equals(Owner.WHITE) ) &&  a.equals(Owner.BLUEWHITE) )
		  return true;
	  if((b.equals(Owner.GREEN) ||b.equals(Owner.YELLOW) ) &&  a.equals(Owner.GREENYELLOW) )
		  return true; 
	  if((b.equals(Owner.RED) ||b.equals(Owner.ORANGE) )  &&  a.equals(Owner.REDORANGE) )
		  return true;
	
	  //wild ball
	  if((a.equals(Owner.WILD)  || a.equals(Owner.BOMB) ) && !b.equals(Owner.NONE)  )
		  return true;
	  if((b.equals(Owner.WILD)  || b.equals(Owner.BOMB) ) && !a.equals(Owner.NONE)  )
		  return true;
	  return false;

	  
  }

  //show the way and move animation
  public static void light(int xx,int yy){
	  if(isEmptyCell(xx,yy) && count ==1 && way(xx,yy,x,y)){
		  for(int i =0;i<9;i++){
			  for(int j =0;j<9;j++){
				  if(record[i][j]!=1 && isEmptyCell(i,j))
					  back[i][j].setValue(Holder.SQUARE);
				  if(record[i][j]==1)//record==1 represent exist one ball
				      back[i][j].setValue(Holder.HIGHLIGHT);
			  }
		  }
  		initRecord();
	  } 
	  else{
		  for(int i =0;i<9;i++){
			  for(int j =0;j<9;j++){
				  if(record[i][j]==1 && isEmptyCell(i,j))
					  back[i][j].setValue(Holder.SQUARE);

			  }
		  }
	  		initRecord();
	  }	 
  }

  
  public static void initback() {
	for (int i = 0; i < BOARD_SIZE; i++) {
        for (int j = 0; j < BOARD_SIZE; j++) { 
        		back[i][j] .setValue(Holder.SQUARE);
        } 
      }	
  }
  
    public static int[][] record = new int[9][9];
    private static void initRecord(){
    	for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) { 
            		record[i][j] = 0;
            } 
          }	
    }

	public static boolean havisited(int x,int y){
		return record[x][y]==1;
	}


	public static int[] path = new int[100] ;
//-------------------------end(x,y)----start(a,b)-----------
	public static boolean way(int x,int y,int a ,int b){
		//clear path[]
	 
		if(!isEmptyCell(x,y)){
			return false;
		}
		int [][]color = new int[9][9];
		int [][]temppath = new int[9][9];

		initRecord();
		int e=a,f=b;
		int []que = new int[100000];
		int front=0;
		int rear=0;
		que[rear]=e*9+f;
		rear++;
		while(front!=rear && !(x==e&&y==f)){
			//front
			e=que[front]/9;
			f=que[front]%9;
			temppath[e][f]=1;
			front++;
			//if its empty into queue
			if(e-1>=0 && isEmptyCell(e-1,f) && temppath[e-1][f]!=1){
				color[e-1][f]=1;//record
				que[rear]=(e-1)*9+f;
				rear++;
    	    if(x==e-1&&y==f){
    	    	e--;
    	    	break;
    	    }
    	}
    	if(f-1>=0 && isEmptyCell(e,f-1) && temppath[e][f-1] !=1){
    		color[e][f-1]=2;
    		que[rear]=e*9+f-1;
    		rear++;
    	    if(x==e&&y==f-1){
    	    	f--;
    	    	break;
    	    }

    	}
    	if(e+1<9 && isEmptyCell(e+1,f) && temppath[e+1][f] !=1){
    		color[e+1][f]=3;
    		que[rear]=(e+1)*9+f;
    		rear++;
    	    if(x==e+1&&y==f){
    	    	e++;
    	    	break;
    	    }

    	}
    	if(f+1<9 && isEmptyCell(e,f+1)&& temppath[e][f+1] !=1){
    		color[e][f+1]=4;
    		que[rear]=e*9+f+1;
    		rear++;
    	    if(x==e&&y==f+1){
    	    	f++;
    	    	break;
    	    }

    	}
    }
    int[] revpath = new int[100];
	if(x==e && y==f){
		int ptr=0;//this pointer used to help build path[]
		while(!(e==a&&f==b)){
			//record the reversed path ,waiting for be reversed to path[]
			
			if(color[e][f]==1){
				revpath[ptr]= e*9+f+1;
				ptr++;
				record[e][f]=1;
				e++;
			}
			if(color[e][f]==2){
				revpath[ptr]= e*9+f+1;
				ptr++;
				record[e][f]=1;
				f++;
			}
			if(color[e][f]==3){
				revpath[ptr]= e*9+f+1;
				ptr++;
				record[e][f]=1;
				e--;
			}
			if(color[e][f]==4){
				revpath[ptr]= e*9+f+1;
				ptr++;
				record[e][f]=1;
				f--;
			}
		}
		revpath[ptr]= e*9+f+1;//add 1 to avoid the case in board[0][0]
		
		int last=0;
		while(revpath[last] !=0)
			last++;
		int length=last;
		path = new int[length];
		last--;
		for(int i=0;i<length;i++){
			path[i]=revpath[last];
			last--;
		}
		
		for(int i=0;i<path.length;i++){
			path[i]--;
			System.out.printf("%d ",revpath[i]);
		}
		System.out.printf("\n ");

	    return true;
	}
	else
		return false;
 }
 
}


